﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SanctusSoftware.Music.Chords;
using System.IO;

namespace SanctusSoftware.Music.ChordSheetEditor.Client
{
  public abstract class ChordSheetEditorPresenterBase
  {

    #region Properties

    private IChordSheetEditorHostView  _HostView = null;
    public IChordSheetEditorHostView HostView
    {
      get { return _HostView; }
      private set { _HostView = value; }
    }

    private IChordSheetEditorActionView _ActionView = null;
    public IChordSheetEditorActionView ActionView
    {
      get { return _ActionView; }
      private set { _ActionView = value; }
    }

    #endregion

    #region Constructor

    public ChordSheetEditorPresenterBase(IChordSheetEditorHostView /*<TDoc>*/ hostView, IChordSheetEditorActionView actionView)
    {
      if (hostView == null)
      {
        throw new ArgumentNullException("hostView");
      }
      if (actionView == null)
      {
        throw new ArgumentNullException("actionView");
      }
      HostView = hostView;
      ActionView = actionView;

      SetupLogging();
      try
      {
        SetupSettings();

        SubscribeToEvents();

        SetupTransposition();
        SetupQuickTransposeButtons();

        // Future
        //SetupCustomDocumentProperties();
        LoadAndApplySettings();
        SetupDevMode();

      }
      catch (Exception ex)
      {
        hostView.ShowMessage("Error Occured in Constructor: " + ex.Message);  
      }

    }

    #endregion


    #region Methods (Setup)

    private void SetupLogging()
    {
      try
      {

        ////// Create Text File Log
        ////TextFileLogDistributor tfld = new TextFileLogDistributor();
        ////FileInfo assemblyFile = new FileInfo(Assembly.GetExecutingAssembly().Location);
        ////DirectoryInfo loggingFolder = new DirectoryInfo(Path.Combine(assemblyFile.DirectoryName, "Log"));
        ////tfld.LogFolder = loggingFolder;
        ////ExceptionLogger.AddLogDistributor("Text File", tfld);

        //// Create Pop-Up for User to Send to Sanctus Software
        //TransmissionLogDistributor tld = new TransmissionLogDistributor();
        //tld.SoftwareProductName = "Chord Sheet Editor for Microsoft® Word® 2007";
        //ExceptionLogger.AddLogDistributor("Transmission", tld);

        //// Global Additional Data
        //ExceptionLogger.AddGlobalAdditionalData("Product", "Chord Sheet Editor for Microsoft® Word® 2007");
        //ExceptionLogger.AddGlobalAdditionalData("Word Version", Application.Version);



      }
      catch (Exception ex)
      {
        // Yeah, and do what?
      }
    }

    private void SetupSettings()
    {
      // Should Do Everything Required
      Settings.GetInstance();
    }

    private void SubscribeToEvents()
    {
      // Target
      ActionView.TargetChanged += ActionView_TargetChanged;

      // Lyrics
      ActionView.RequestRemoveChords += ChordSheetRibbon_RequestRemoveChords;
      //Globals.Ribbons.ChordSheetRibbon.downloadLyricsButton.Click += new EventHandler<RibbonControlEventArgs>(downloadLyricsButton_Click);

      // Chords
      ActionView.RequestTranspose += View_RequestTranspose;
      ActionView.RequestTransposeOptions += View_RequestTransposeOptions;
      ActionView.RequestQuickTransposeDownLow += ChordSheetRibbon_RequestQuickTransposeDownLow;
      ActionView.RequestQuickTransposeDownHigh += ChordSheetRibbon_RequestQuickTransposeDownHigh;
      ActionView.RequestQuickTransposeEnharmonic += ChordSheetRibbon_RequestQuickTransposeEnharmonic;
      ActionView.RequestQuickTransposeUpLow += ChordSheetRibbon_RequestQuickTransposeUpLow;
      ActionView.RequestQuickTransposeUpHigh += ChordSheetRibbon_RequestQuickTransposeUpHigh;

      ActionView.RequestFormatChords += ChordSheetEditorPresenterBase_RequestFormatChords;
      ActionView.RequestRemoveHyperlinksFromChordLines += ChordSheetRibbon_RequestRemoveHyperlinksFromChordLines;


      // Globals.Ribbons.ChordSheetRibbon.addChordDiagramsButton.Click += new EventHandler<RibbonControlEventArgs>(addChordDiagramsButton_Click);

      ActionView.OriginalKeyChanged += View_OriginalKeyChanged;

      // Custom Properties/Fields/Metadata
      //Globals.Ribbons.ChordSheetRibbon.chordSheetInformationPropertiesButton.Click += new EventHandler<RibbonControlEventArgs>(chordSheetInformationPropertiesButton_Click);

      // Debugging
      //Globals.Ribbons.ChordSheetRibbon.showSentencesButton.Click += new EventHandler<RibbonControlEventArgs>(showSentencesButton_Click);
      //Globals.Ribbons.ChordSheetRibbon.showSoftSentencesButton.Click += new EventHandler<RibbonControlEventArgs>(showSoftSentencesButton_Click);

      //Globals.Ribbons.ChordSheetRibbon.formatChordsButton.Click += new EventHandler<RibbonControlEventArgs>(formatChordsButton_Click);
      //Globals.Ribbons.ChordSheetRibbon.makeLyricSheetButton.Click += new EventHandler<RibbonControlEventArgs>(makeLyricSheetButton_Click);
      //Globals.Ribbons.ChordSheetRibbon.transposeButton.Click += new EventHandler<RibbonControlEventArgs>(transposeButton_Click);
      //Globals.Ribbons.ChordSheetRibbon.originalKeyDropDown.SelectionChanged += new EventHandler<RibbonControlEventArgs>(originalKeyDropDown_SelectionChanged);
      //Globals.Ribbons.ChordSheetRibbon.quickTransposeDownOneLowButton.Click += new EventHandler<RibbonControlEventArgs>(downOne1Button_Click);
      //Globals.Ribbons.ChordSheetRibbon.quickTransposeDownOneHighButton.Click += new EventHandler<RibbonControlEventArgs>(downOne2Button_Click);
      //Globals.Ribbons.ChordSheetRibbon.quickTransposeEnharmonicButton.Click += new EventHandler<RibbonControlEventArgs>(enharmonicButton_Click);
      //Globals.Ribbons.ChordSheetRibbon.quickTransposeUpOneLowButton.Click += new EventHandler<RibbonControlEventArgs>(upOne1Button_Click);
      //Globals.Ribbons.ChordSheetRibbon.quickTransposeUpOneHighButton.Click += new EventHandler<RibbonControlEventArgs>(upOne2Button_Click);



    }

    private void SetupTransposition()
    {
      ActionView.ValidKeys = Key.Keys;
    }

    private void SetupQuickTransposeButtons()
    {
      if (ActionView.OriginalKey != null)
      {
        string originalKey = ActionView.OriginalKey;

        // Keys to Transpose Up One Semitone
        Note upOneNote = NoteTransposer.GetRelativeNote(originalKey, 1);

        List<string> upKeys = new List<string>(upOneNote.NoteNames);
        upKeys.RemoveAll(delegate(string item)
        {
          return !Key.Keys.Contains<string>(item);
        });

        if (upKeys.Count >= 1)
        {
          ActionView.QuickTransposeUpLowKey = upKeys[0];
          ActionView.IsQuickTransposeUpLowEnabled = true;
        }
        else
        {
          ActionView.QuickTransposeUpLowKey = "";
          ActionView.IsQuickTransposeUpLowEnabled = false;

        }

        if (upKeys.Count >= 2)
        {
          ActionView.QuickTransposeUpHighKey = upKeys[1];
          ActionView.IsQuickTransposeUpHighEnabled = true;
        }
        else
        {
          ActionView.QuickTransposeUpHighKey = "";
          ActionView.IsQuickTransposeUpHighEnabled = false;
        }

        // Keys to Transpose Down One Semitone
        Note downOneNote = NoteTransposer.GetRelativeNote(originalKey, -1);

        List<string> downKeys = new List<string>(downOneNote.NoteNames);
        downKeys.RemoveAll(delegate(string item)
        {
          return !Key.Keys.Contains<string>(item);
        });

        if (downKeys.Count >= 1)
        {
          ActionView.QuickTransposeDownLowKey = downKeys[0];
          ActionView.IsQuickTransposeDownLowEnabled = true;
        }
        else
        {
          ActionView.QuickTransposeDownLowKey = "";
          ActionView.IsQuickTransposeDownLowEnabled = false;
        }

        if (downKeys.Count >= 2)
        {
          ActionView.QuickTransposeDownHighKey = downKeys[1];
          ActionView.IsQuickTransposeDownHighEnabled = true;
        }
        else
        {
          ActionView.QuickTransposeDownHighKey = "";
          ActionView.IsQuickTransposeDownHighEnabled = false;
        }

        // Enharmonic Key
        Note enharmonicNote = NoteTransposer.GetRelativeNote(originalKey, 0);

        List<string> enharmonicKeys = new List<string>(enharmonicNote.NoteNames);
        enharmonicKeys.RemoveAll(delegate(string item)
        {
          return !Key.Keys.Contains<string>(item);
        });
        enharmonicKeys.RemoveAll(delegate(string item)
        {
          return (item == originalKey);
        });

        if (enharmonicKeys.Count >= 1)
        {
          ActionView.QuickTransposeEnharmonicKey = enharmonicKeys[0];
          ActionView.IsQuickTransposeEnharmonicEnabled = true;
        }
        else
        {
          ActionView.QuickTransposeEnharmonicKey = "";
          ActionView.IsQuickTransposeEnharmonicEnabled = false;
        }

      }
      else
      {
        // Disable Buttons
        ActionView.IsQuickTransposeDownLowEnabled = true;
        ActionView.IsQuickTransposeDownHighEnabled = false;
        ActionView.IsQuickTransposeEnharmonicEnabled = false;
        ActionView.IsQuickTransposeUpLowEnabled = false;
        ActionView.IsQuickTransposeUpHighEnabled = false;
      }
    }

    private void LoadAndApplySettings()
    {
      try
      {
        ActionView.Target = Settings.GetInstance().Target;
      }
      catch (Exception ex)
      {
        //ExceptionLogger.Log(ex);
      }
    }

    private void SetupDevMode()
    {
      try
      {
        bool isDevMode = File.Exists("dev.txt");
        ActionView.IsDevMode = isDevMode;
      }
      catch (Exception ex)
      {
        HostView.ShowMessage("Error Occured in SetupDevMode(): " + ex.Message);
      }
    }

    #endregion

    #region Format Chords

    private void FormatChordLines(IRichTextFormat format, ISentence[] sentencesToFormat)
    {


      ISentence[] chordSentences = this.GetChordSentences(sentencesToFormat); // sentencesToFormat

      foreach (ISentence sentence in chordSentences)
      {

        if (format.SetFontName)
        {
          sentence.Font.Name = format.FontNameValue;
        }

        if (format.SetFontSize)
        {
          sentence.Font.Size = format.FontSizeValue;
        }

        if (format.SetFontColor)
        {
          sentence.Font.Color = format.FontColorValue;
        }

        if (format.SetBold)
        {
          sentence.Font.Bold = format.BoldValue;
        }

        if (format.SetItalic)
        {
          sentence.Font.Italic = format.ItalicValue;
        }

        if (format.SetUnderline)
        {
          sentence.Font.Underline = format.UnderlineValue;
        }

      }
    }

    #endregion

    #region Methods (Transpose)

    private void Transpose(ISentence[] sentences, string originalKey, string transposeTo)
    {

      try
      {

        HostView.AllowScreenUpdating = false;

        // Cannot Transpose With Hyperlinks
        if (sentences.Any(sentence => sentence.ContainsHyperlinks && IsChordSentence(sentence)))
        {

          if (HostView.PromptRemoveHyperlinksFromChords())
          {
            // Clear Hyperlinks
            HostView.Status = "Removing Chord Hyperlinks";
            foreach (var sentence in GetChordSentences(sentences))
            {
              sentence.RemoveHyperlinks();
            }
            HostView.Status = "Chord Hyperlinks Removed";
          }
          else
          {
            return;
          }


        }

        ISentence[] chordSentences = GetChordSentences(sentences);

        var temp = from cs in chordSentences
                   select cs.GetChordWords();
        IWord[] chordsToTranspose = new IWord[0];
        foreach (var item in temp)
        {
          chordsToTranspose = chordsToTranspose.Union(item).ToArray();
        }

        for (int i = 0; i < chordsToTranspose.Length; i++)
        {
          HostView.Status = "Transposing Chord " + (i + 1).ToString() + " of " + chordsToTranspose.Length.ToString();

          IWord chordToTranspose = chordsToTranspose[i];
          chordToTranspose.Text = ChordTransposer.GetTransposedChord(chordToTranspose.Text, originalKey, transposeTo);

        }

        // Reselect the sentences in CurrentSelection mode
        if (ActionView.Target == ActionTarget.CurrentSelection && sentences.Length > 0)
        {
          HostView.SelectRange(sentences.First(), sentences.Last());
        }
        HostView.Status = "Done Transposing";

      }
      catch (ChordSentenceContainsHyperlinksException cschex)
      {
        // Need friendlier message, maybe option to remove hyperlinks
        HostView.ShowMessage("One or more Chord Lines contain Hyperlinks. Transposition cannot continue.");
      }
      catch (Exception ex)
      {
        HostView.Status = "Transposing Failed";
        throw;
      }
      finally
      {
        HostView.AllowScreenUpdating = true;
      }


    }


    #endregion

    #region Methods (Quick Transpose)

    private void QuickTranspose(string originalKey, string transposeTo)
    {

      try
      {

        ISentence[] sentences = GetTargetSentences(true);

        if (sentences == null)
        {
          return;
        }

        Transpose(sentences, originalKey, transposeTo);

        if (ActionView.Target == ActionTarget.ThisDocument || ActionView.Target == ActionTarget.CurrentSelection || ActionView.Target == ActionTarget.CurrentPage)
        {
          ActionView.OriginalKey = transposeTo;
          SetupQuickTransposeButtons();
        }

      }
      catch (Exception ex)
      {
        //ExceptionLogger.Log(ex);
        HostView.ShowMessage("Error Occured in QuickTranspose(): " + ex.Message);
        HostView.Status = "Transpose Failed";
      }

    }

    void ChordSheetRibbon_RequestQuickTransposeDownLow(object sender, EventArgs e)
    {
      string originalKey = ActionView.OriginalKey;
      string transposeTo = ActionView.QuickTransposeDownLowKey;
      QuickTranspose(originalKey, transposeTo);
    }

    void ChordSheetRibbon_RequestQuickTransposeDownHigh(object sender, EventArgs e)
    {
      string originalKey = ActionView.OriginalKey;
      string transposeTo = ActionView.QuickTransposeDownHighKey;
      QuickTranspose(originalKey, transposeTo);
    }

    void ChordSheetRibbon_RequestQuickTransposeEnharmonic(object sender, EventArgs e)
    {
      string originalKey = ActionView.OriginalKey;
      string transposeTo = ActionView.QuickTransposeEnharmonicKey;
      QuickTranspose(originalKey, transposeTo);
    }

    void ChordSheetRibbon_RequestQuickTransposeUpLow(object sender, EventArgs e)
    {
      string originalKey = ActionView.OriginalKey;
      string transposeTo = ActionView.QuickTransposeUpLowKey;
      QuickTranspose(originalKey, transposeTo);
    }

    void ChordSheetRibbon_RequestQuickTransposeUpHigh(object sender, EventArgs e)
    {
      string originalKey = ActionView.OriginalKey;
      string transposeTo = ActionView.QuickTransposeUpHighKey;
      QuickTranspose(originalKey, transposeTo);
    }

    #endregion

    #region Methods (Remove Chord Lines)

    private void RemoveChordLines(ISentence[] sentences)
    {

      try
      {

        HostView.AllowScreenUpdating = false;

        HostView.Status = "Retrieving Chord Lines...";
        ISentence[] chordSentences = GetChordSentences(sentences); // sentences.Where(sentence => ChordSentenceHelper.GetChordSentences(sentences, Settings.GetInstance().MinimumChordLineMatchPercentage);

        HostView.Status = "Removing Chord Lines...";
        for (int i = chordSentences.Length - 1; i >= 0; i--)
        {
          int current = Math.Abs((i + 1) - chordSentences.Length) + 1;
          HostView.Status = "Removing Chord Line " + current.ToString() + " of " + chordSentences.Length.ToString();
          chordSentences[i].Delete();
        }
        HostView.Status = "Done Removing Chords";

      }
      catch (Exception)
      {
        HostView.Status = "Failed to Remove Chords";
        throw;
      }
      finally
      {
        HostView.AllowScreenUpdating = true;
      }

    }

    #endregion

    #region Methods (Abstract)

    protected abstract ISentence[] GetTargetSentences(bool copyContentsToNewDocument);

    #endregion

    #region Event Handlers (Action Target)

    void ActionView_TargetChanged(object sender, EventArgs e)
    {
      try
      {
        Settings.GetInstance().Target = ActionView.Target;
        Settings.SaveInstance();

      }
      catch (Exception ex)
      {
        HostView.ShowMessage("Error Occured in ActionView_TargetChanged: " + ex.Message);
      }
    }

    #endregion

    #region Event Handlers (Chords)

    void ChordSheetEditorPresenterBase_RequestFormatChords(object sender, EventArgs e)
    {

      try
      {

        IRichTextFormat existingFormat = null;
        ISentence exampleSentence = GetFirstChordSentence();

        if (exampleSentence != null)
        {
          existingFormat = new RichTextFormat();
          existingFormat.FontNameValue = exampleSentence.Font.Name;
          existingFormat.FontSizeValue = Convert.ToDecimal(exampleSentence.Font.Size);
          existingFormat.FontColorValue = exampleSentence.Font.Color; // existingFormat.AvailableColors.FirstOrDefault(item => (WdColor)item.Tag == exampleFont.Color);
          existingFormat.BoldValue = exampleSentence.Font.Bold;
          existingFormat.ItalicValue = exampleSentence.Font.Italic;
          existingFormat.UnderlineValue = exampleSentence.Font.Underline;
        }

        IRichTextFormat newFormat = HostView.GetTextFormat(existingFormat);
        if (newFormat != null)
        {
          ISentence[] sentencesToFormat = GetTargetSentences(true);
          FormatChordLines(newFormat, sentencesToFormat);
        }

      }
      catch (Exception ex)
      {
        //ExceptionLogger.Log(ex);
        HostView.ShowMessage("Error in ChordSheetEditorPresenterBase_RequestFormatChords: " + ex.Message);
      }
    }

    void ChordSheetRibbon_RequestRemoveHyperlinksFromChordLines(object sender, EventArgs e)
    {
      try
      {
        ISentence[] sentences = GetTargetSentences(true);
        foreach (var sentence in GetChordSentences(sentences))
        {
          sentence.RemoveHyperlinks();
        }
      }
      catch (Exception ex)
      {
        //ExceptionLogger.Log(ex);
      }
    }

    void ChordSheetRibbon_RequestRemoveChords(object sender, EventArgs e)
    {
      try
      {
        //Document doc = GetDocument(true);
        ISentence[] sentences = GetTargetSentences(true);
        this.RemoveChordLines(sentences);
      }
      catch (Exception ex)
      {
        //ExceptionLogger.Log(ex);
        HostView.ShowMessage(ex.Message);
        HostView.Status = "Make Lyric Sheet Failed";
      }
    }

    #endregion

    #region Event Handlers (Transpose)

    void View_OriginalKeyChanged(object sender, EventArgs e)
    {
      try
      {
        SetupQuickTransposeButtons();
      }
      catch (Exception ex)
      {
        //ExceptionLogger.Log(ex);
        HostView.ShowMessage("Error Occured in View_OriginalKeyChanged: " + ex.Message);
        // TODO: Disable one up/down buttons?
      }
    }

    void View_RequestTranspose(object sender, EventArgs e)
    {
      try
      {
        // http://word.tips.net/Pages/T000749_Turning_Off_Screen_Updating.html
        HostView.AllowScreenUpdating = false;

        //Document doc = GetDocument(true);
        ISentence[] sentences = GetTargetSentences(true);

        if (sentences == null)
        {
          return;
        }

        string originalKey = ActionView.OriginalKey;
        string transposeTo = ActionView.TransposeTo;

        Transpose(sentences, originalKey, transposeTo);

        if (ActionView.Target == ActionTarget.ThisDocument || ActionView.Target == ActionTarget.CurrentSelection)
        {
          ActionView.OriginalKey = transposeTo;
          SetupQuickTransposeButtons();
        }

      }
      catch (Exception ex)
      {
        //ExceptionLogger.Log(ex);
        HostView.ShowMessage("Error Occured in View_RequestTranspose: " + ex.Message);
        HostView.Status = "Transpose Failed";
      }
      finally
      {
        HostView.AllowScreenUpdating = true;
      }
    }

    void View_RequestTransposeOptions(object sender, EventArgs e)
    {
      try
      {

        int? result = HostView.PromptMinimumChordLineMatchPercentage(Settings.GetInstance().MinimumChordLineMatchPercentage);
        if (result != null)
        {
          Settings.GetInstance().MinimumChordLineMatchPercentage = result.Value;
          Settings.SaveInstance();
        }

      }
      catch (Exception ex)
      {
        HostView.ShowMessage("Error Occured in View_RequestTransposeOptions: " + ex.Message);
      }
    }
    
    #endregion


    #region Helpers

    private ISentence GetFirstChordSentence()
    {
      return this.HostView.Sentences
        .Where(sentence => IsChordSentence(sentence))
        .FirstOrDefault();
    }

    private ISentence[] GetChordSentences()
    {
      return GetChordSentences(this.HostView.Sentences);
    }

    private ISentence[] GetChordSentences(ISentence[] sentences)
    {
      return sentences
        .Where(sentence => IsChordSentence(sentence))
        .ToArray();
    }

    private bool IsChordSentence(ISentence sentence)
    {
      return SentenceTransposer.IsChordLine(sentence, Settings.GetInstance().MinimumChordLineMatchPercentage);
    }

    #endregion

  }
}
